今天來提一個大家都說學Go一定要學的東西,goroutine
,雖然菜雞如我還沒理解到用途,不過學起來一定不是一件壞事的吧。
白話一點可以都叫做同時進行,但同時又可以細分為「時間內完成多項任務」與「同一時間做多項任務」
Concurrency
:共享時間運算,在一段時間內輪流享有時間資源Parallelism
:平行運算,一直都能享有時間資源聽不懂對吧?那再舉個例子
這篇文章講的滿清楚的,有興趣可以看一下ˊˇˋ
Goroutine
是個輕量的執行序。
A goroutine is a lightweight thread managed by the Go runtime.
直接使用一次就知道了,只要使用go
關鍵字去執行func
,就能建立一個新的goroutine
。
package main
import (
"fmt"
"time"
)
func Print() {
for i := 0; ; i++ {
fmt.Println("func Print()")
time.Sleep(1 * time.Second)
}
}
func main() {
go Print()
fmt.Println("main")
}
output:
main
明明Print()
內寫的是無窮迴圈,程式卻print
個main
就結束了,這是因為main()
主程式執行完後會中斷所有goroutine
,因此Print()
來不及執行就被中斷了。
將main()
裡面改成下面這樣,幫程式加個暫停器吧
func main() {
go Print()
for i := 0; i < 2; i++ {
fmt.Println("main")
time.Sleep(time.Second)
}
fmt.Println("main end")
}
output:
main
func Print()
func Print()
main
main end
可以看到main()
與print()
是同時並交錯進行的。多執行幾次還會發現每次結果會不同。
Go在正常情況下會將goroutine
分配給不同的邏輯處理器執行,實現程式的並行
func main() {
go Print("X")
go Print("O")
time.Sleep(time.Second * 2)
}
func Print(s string) {
for i := 0; i < 50; i++ {
fmt.Print(s)
}
}
output:
OOOOXXXXXXXXXXOXXXXXXXOOOOOOOOOOOOOOOOOOXXXXXXXOXXXXXXXXXXXXXXXXOOOOOOOOOOXXXXXXXOOOOOOOOOOOOOOOOXXX
會看到兩個Print()
同時都在print
東西,是因為goroutine
被分配到兩個處理器,同時在執行的關係。
可以使用runtime.GOMAXPROCS(1)
來限制Go能使用的處理器數量,才能看到併發的案例,試試看吧
func main() {
runtime.GOMAXPROCS(1) // 限制能使用處理器=1
go Print("X")
go Print("O")
time.Sleep(time.Second * 2)
}
func Print(s string) {
for i := 0; i < 50; i++ {
fmt.Print(s)
}
}
output:
OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
從這個例子就看得出單處理器狀況下,會先將手上的goroutine
處理完,才處理下一個goroutine
。
在執行序進入等待狀態時,會先切換到另一個執行序讓他執行,以免浪費時間。我在print
內設個time.Sleep(),
來看兩個goroutine
互相切換執行的樣子。
func main() {
runtime.GOMAXPROCS(1)
go Print("X")
go Print("O")
time.Sleep(time.Second * 2)
}
func Print(s string) {
for i := 0; i < 50; i++ {
time.Sleep(time.Microsecond * 1)
fmt.Print(s)
}
}
output:
XOOXXOOXXOOXXOOXXOOXXOOXXOOXXOOXXOOXXOOXXOOXXOOXXOOXXOOXXOOXXOOXXOOXXOOXXOOXXOOXXOOXXOOXXOOXXOOXXOOX
其中一個goroutine
在等待期間時,會交換到另一個goroutine
執行,可以看到這邊雖然限制了CPU
,卻還是交錯著執行,這邊就是併發的案例。
上面的例子都是自己設time.Sleep()
,實際寫程式時不可能這樣預估時間,因此就要讓程式自己等待排程結束才關閉,就會用到其他套件裡的東西。
WaitGroup
的三個方法:
Add(i int)
: 計數器增加i
Done()
: 計數器-1
,相當於Add(-1)
Wait()
: 阻塞直到所有的WaitGroup
數量變為零,即計數器變為0var wg sync.WaitGroup // 建立全域變數wg
func main() {
runtime.GOMAXPROCS(1) // 設置CPU使用
wg.Add(2) // 添加2筆goroutine訂單
go Print("X")
go Print("O")
fmt.Println("waiting to finish")
wg.Wait() // 等待WaitGroup執行完成
fmt.Println("\nfinish Program")
}
func Print(s string) {
defer wg.Done() // 操作了一次WaitGroup,要讓wg-1
for i := 0; i < 50; i++ {
time.Sleep(time.Microsecond * 1)
fmt.Print(s)
}
}
output:
waiting to finish
XOOXXOOXXOOXXOOXXOOXXOXXOOXXOOXXOOXXOOXXOOXXOOXXOOXXOOXXOOXXOOXXOOXXOOXXOOXXOOXXOOXXOOXXOOXXOOXXOOXO
finish Program
最開始先宣告WaitGroup
變數,將要等待的goroutine
數量加入,最後等待goroutine
都執行完。
參賽前以為每天花個一小時整理文章就好,現在讀懂+整理成文章的時間越來越多,好可怕。今天一直卡在goroutine
明明說是併發,執行起來卻像並行,看了好多的案例才看懂,希望我有將我理解到的寫在這篇文章裡ˊˇˋ
Golang goroutine
https://ithelp.ithome.com.tw/articles/10204971
Goroutine 讓你用少少的線程, 能接受更多的工作, 但沒說會作比較快
https://ithelp.ithome.com.tw/articles/10218483
Day14 Go併發症狀- Goroutines (go)
https://ithelp.ithome.com.tw/articles/10240892
Day 23 協程同步的三個方法 - WaitGroup
https://ithelp.ithome.com.tw/articles/10226126
使用Golang打造Web應用程式
https://willh.gitbook.io/build-web-application-with-golang-zhtw/02.0/02.7
Golang 入門系列(十五)如何理解go的併發?
https://www.796t.com/content/1568963107.html